#include <iostream>
#include <memory>
#include <vector>
#include <limits>
#include <algorithm>
#include <unistd.h>
#include <chrono>

using namespace std;

template<typename V, typename T>
ostream& operator << (ostream& os, const chrono::duration<V,T>& d){
    os << "[" << d.count() << " of " << T::num << "/" << T::den << " seconds]";
    return os;
}

template<typename V, typename T>
ostream& operator << (ostream& os, const pair<V,T>& p){
    os << "[" << p.first << ", " << p.second << "]" << endl;
    return os;
}

template <typename C>
void printClockData(){
    using namespace std;

    cout<<"- precision: ";
    typedef typename C::period P;
    if (ratio_less_equal<P, milli>::value){
        typedef typename ratio_multiply<P, kilo>::type TT;
        cout<< double(TT::num) / TT::den << " milliseconds"<<endl;
    }
    else{
        cout<<double(P::num) / P::den<<"seconds"<<endl;
    }
    cout<<"- is_steady: "<<C::is_steady<<endl;
}

string tp_to_string(chrono::system_clock::time_point& tp){
    time_t t = chrono::system_clock::to_time_t(tp);
    string ts = ctime(&t);
    ts.resize(ts.length()-1);
    return ts;
}


int clock_timer_test() {
    /*
    // C style function for current time
    while (1) {
        time_t timer;
        timer = time(nullptr);
        struct tm *gmt_time;
        struct tm *local_time;
        gmt_time = gmtime(&timer);
        local_time = localtime(&timer);
        printf("%02u:%02u:%02u\n", gmt_time->tm_hour, gmt_time->tm_min, gmt_time->tm_sec);
        sleep(1);
    }
     */
    // Duration means a time range, represented by a time unit and an amount
    chrono::duration<int> twentySeconds(20);
    chrono::duration<int, ratio<1,1000>> milliSeconds(10);
    chrono::duration<int, ratio<1,1000000>> microSeconds(10);
    chrono::duration<double, ratio<60>> ninetySeconds(1.5);

    // Some types are defined
    // You can only use int64_t for them
    chrono::nanoseconds nanoseconds;
    chrono::microseconds microseconds;
    chrono::milliseconds milliseconds;
    chrono::seconds seconds;
    chrono::minutes minutes;
    chrono::hours hours;

    // you can use + - * / % for all this

    // you can cast them as you wish
    auto s = chrono::seconds(time(nullptr));
    auto ns = chrono::nanoseconds(s);
    auto ns2 = chrono::duration_cast<chrono::nanoseconds>(s);
    printf("seconds: %lld, nanoseconds: %lld, %lld\n", s, ns, ns2);

    cout<<ns2<<endl;

    auto p = pair<int, string>(1, "This is string for 1");
    cout<<p<<endl;


    cout<<"system_clock: "<<endl;
    printClockData<chrono::system_clock>();
    cout<<"high_resolution_clock: "<<endl;
    printClockData<chrono::high_resolution_clock>();
    cout<<"steady_clock: "<<endl;
    printClockData<chrono::steady_clock>();


    auto start = chrono::system_clock::now();
    usleep(1050500);
    auto end = chrono::system_clock::now();
    auto internal = chrono::duration_cast<chrono::microseconds>(end - start);

    cout<<internal<<endl;

    chrono::system_clock::time_point tp = chrono::system_clock::now();
    cout<<tp_to_string(tp)<<endl;
}



